/***************************** 
*
*   驱动程序模板
*   版本：V1
*   使用方法(末行模式下)：
*   :%s/mydrv/"你的驱动名称"/g
*
*******************************/

#include <asm/uaccess.h>
#include <linux/aio.h>
#include <linux/backing-dev.h>
#include <linux/bootmem.h>
#include <linux/capability.h>
#include <linux/crash_dump.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/highmem.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/module.h>
#include <linux/pfn.h>
#include <linux/ptrace.h>
#include <linux/random.h>
#include <linux/raw.h>
#include <linux/slab.h>
#include <linux/splice.h>
#include <linux/tty.h>
#include <linux/vmalloc.h>

#define RS485IO_ON 1
#define RS485IO_OFF 0
#define WD_ON 3
#define WD_OFF 2
#define POWER_ON 10
#define POWER_OFF 11
#define LTE_POWER_ON 12
#define LTE_POWER_OFF 13
/*
RS485引脚是pcb上第72脚，GE1_TXEN,GPIO#28 则偏移位为28-24=4 位于GPIO_39-24
喂狗io是pcb上73脚，GE1_TXCLK,GPIO#39,偏移位5
power电源控制引脚pcb第65引脚，GE2_TXD0,GPIO#60,偏移位60-40=20,位于GPIO_71_40
LTE电源控制引脚为板上第66脚，GE2_TXEN,GPIO#64，偏移位24
*/

volatile unsigned long* GPIOMODE;
volatile unsigned long* GPIO71_40_DIR;
volatile unsigned long* GPIO71_40_DATA;

volatile unsigned long* GPIO39_24_DIR; //设置引脚的方向
volatile unsigned long* GPIO39_24_DATA; //写引脚数据
//volatile unsigned long *GPIO23_00_DATA;
//volatile unsigned long *GPIO23_00_DIR;
/****************  基本定义 **********************/
//内核空间缓冲区定义
#if 0
#define KB_MAX_SIZE 20
#define kbuf [KB_MAX_SIZE];
#endif

//加密函数参数内容： _IOW(IOW_CHAR , IOW_NUMn , IOW_TYPE)
//加密函数用于mydrv_ioctl函数中
//使用举例：ioctl(fd , _IOW('L',0x80,long) , 0x1);
//#define NUMn mydrv , if you need!
#define IOW_CHAR 'L'
#define IOW_TYPE long
#define IOW_NUM1 0x80

//初始化函数必要资源定义
//用于初始化函数当中
//device number;
dev_t dev_num;
//struct dev
struct cdev mydrv_cdev;
//auto "mknode /dev/mydrv c dev_num minor_num"
struct class* mydrv_class = NULL;
struct device* mydrv_device = NULL;

/**************** 结构体 file_operations 成员函数 *****************/
//open
static int mydrv_open(struct inode* inode, struct file* file)
{
    //	printk("mydrv drive open...\n");

    //	*GPIO71_40_DATA &=~((1<<28));

    return 0;
}

//close
static int mydrv_close(struct inode* inode, struct file* file)
{
    //	printk("mydrv drive close...\n");

    return 0;
}

//read
static ssize_t mydrv_read(struct file* file, char __user* buffer,
    size_t len, loff_t* pos)
{
    int ret_v = 0;
    //	printk("mydrv drive read...\n");

    return ret_v;
}

//write
static ssize_t mydrv_write(struct file* file, const char __user* buffer,
    size_t len, loff_t* offset)
{
    int ret_v = 0;
    //	printk("mydrv drive write...\n");

    printk("GPIOMODE:%X,71-40_GPIO_DIR:%X,71-40_GPIO_DATA:%X,", *GPIOMODE, *GPIO71_40_DIR, *GPIO71_40_DATA);
    printk("39-24GPIO_DIR:%X,39-24GPIO_DATA:%X\n",*GPIO39_24_DIR, *GPIO39_24_DATA);
    return ret_v;
}

//unlocked_ioctl
static int mydrv_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)
{
    int ret_v = 0;
    //printk("mydrv drive ioctl...\n");

    switch (cmd) {
    //常规：
    //cmd值自行进行修改
    /*		 case MYLEDS_LED1_ON:
			printk("mydrv drive ioctl. on \n");
		    *GPIO71_40_DATA |= (0x1<<28);
		 break;

		 case MYLEDS_LED1_OFF:
            printk("mydrv drive ioctl. off \n");
			*GPIO71_40_DATA &= ~(0x1<<28);
		 break;
*/
    case RS485IO_ON:
        *GPIO39_24_DATA |= (1 << 4);
        break;

    case RS485IO_OFF:
        *GPIO39_24_DATA &= ~(1 << 4);
        break;

    case WD_ON:
        *GPIO39_24_DATA |= (1 << 5);
        break;

    case WD_OFF:
        *GPIO39_24_DATA &= ~(1 << 5);
        break;
    case POWER_ON:
        *GPIO71_40_DATA |= (1 << 20);
        break;
    case POWER_OFF:
        *GPIO71_40_DATA &= ~(1 << 20);
        break;
    case LTE_POWER_ON:
        *GPIO71_40_DATA |= (1 << 24);
        break;
    case LTE_POWER_OFF:
        *GPIO71_40_DATA &= ~(1 << 24);
        break;
    //带密码保护：
    //请在"基本定义"进行必要的定义
    case _IOW(IOW_CHAR, IOW_NUM1, IOW_TYPE): {
        if (arg == 0x1) //第二条件
        {
        }

    } break;

    default:
        break;
    }
    //	printk("DATA-->GPIOMODE:%X,GPIO_DIR:%X,GPIO_DATA:%X\n",*GPIOMODE,*GPIO71_40_DIR,*GPIO71_40_DATA);
    //	printk("DATA0-->GPIO_DIR:%X,GPIO_DATA:%X\n",*GPIO23_00_DIR,*GPIO23_00_DATA);

    return ret_v;
}

/***************** 结构体： file_operations ************************/
//struct
static const struct file_operations mydrv_fops = {
    .owner = THIS_MODULE,
    .open = mydrv_open,
    .release = mydrv_close,
    .read = mydrv_read,
    .write = mydrv_write,
    .unlocked_ioctl = mydrv_ioctl,
};

/*************  functions: init , exit*******************/
//条件值变量，用于指示资源是否正常使用
unsigned char init_flag = 0;
unsigned char add_code_flag = 0;

//init
static __init int mydrv_init(void)
{
    int ret_v = 0;
    printk("mydrv drive init...\n");

    //函数alloc_chrdev_region主要参数说明：
    //参数2： 次设备号
    //参数3： 创建多少个设备
    if ((ret_v = alloc_chrdev_region(&dev_num, 0, 1, "mydrv")) < 0) {
        goto dev_reg_error;
    }
    init_flag = 1; //标示设备创建成功；

    printk("The drive info of mydrv:\nmajor: %d\nminor: %d\n",
        MAJOR(dev_num), MINOR(dev_num));

    cdev_init(&mydrv_cdev, &mydrv_fops);
    if ((ret_v = cdev_add(&mydrv_cdev, dev_num, 1)) != 0) {
        goto cdev_add_error;
    }

    mydrv_class = class_create(THIS_MODULE, "mydrv");
    if (IS_ERR(mydrv_class)) {
        goto class_c_error;
    }

    mydrv_device = device_create(mydrv_class, NULL, dev_num, NULL, "mydrv");
    if (IS_ERR(mydrv_device)) {
        goto device_c_error;
    }
    printk("auto mknod success!\n");

    //------------   请在此添加您的初始化程序  --------------//

    GPIOMODE = (volatile unsigned long*)ioremap(0x10000060, 4);
    GPIO71_40_DIR = (volatile unsigned long*)ioremap(0x10000674, 4);
    GPIO71_40_DATA = (volatile unsigned long*)ioremap(0x10000670, 4);

    GPIO39_24_DIR = (volatile unsigned long*)ioremap(0x1000064C, 4);
    GPIO39_24_DATA = (volatile unsigned long*)ioremap(0x10000648, 4);

    //	GPIO23_00_DATA=(volatile unsigned long *)ioremap(0x10000620,4);
    //	GPIO23_00_DIR=(volatile unsigned long *)ioremap(0x10000624,4);

    printk("ADDR-->GPIOMODE:%X,GPIO_DIR:%X,GPIO_DATA:%X\n", GPIOMODE, GPIO39_24_DIR, GPIO39_24_DATA);
    printk("71-40-->GPIOMODE:%X,GPIO_DIR:%X,GPIO_DATA:%X\n", *GPIOMODE, *GPIO71_40_DIR, *GPIO71_40_DATA);
    //	printk("DATA0-->GPIO_DIR:%X,GPIO_DATA:%X\n",*GPIO23_00_DIR,*GPIO23_00_DATA);
    // printk("DATA2-->GPIOMODE:%X,GPIO_DIR:%X,GPIO_DATA:%X\n", *GPIOMODE, *GPIO39_24_DIR, *GPIO39_24_DATA);

    *GPIOMODE |= (0x1 << 9); //for GRMII1 to GPIO mode
    *GPIOMODE |= (0x1 << 10); //for GRMII2 to GPIO mode
    *GPIO71_40_DIR |= (0x1 << 20); //GPIO#60 set to output
    *GPIO71_40_DIR |= (0x1 << 24); //GPIO#64 set to output

    *GPIO39_24_DIR |= (0x1 << 4);
    *GPIO39_24_DIR |= (0x1 << 5);

    *GPIO39_24_DATA |= (1 << 4);
    *GPIO39_24_DATA |= (1 << 5);
    *GPIO71_40_DATA &= ~(1 << 20); //reset GPIO#60
    *GPIO71_40_DATA &= ~(1 << 24); //reset GPIO#64
    //	*GPIO23_00_DIR |= (0x1<<0);
    //	*GPIO23_00_DATA |= (0x1<<0);

    //	*GPIO71_40_DIR |= (1<<4);
    printk("71-40GPIOMODE:%X,GPIO_DIR:%X,GPIO_DATA:%X\n", *GPIOMODE, *GPIO71_40_DIR, *GPIO71_40_DATA);
    printk("DATA2-->GPIOMODE:%X,GPIO_DIR:%X,GPIO_DATA:%X\n", *GPIOMODE, *GPIO39_24_DIR, *GPIO39_24_DATA);
    //	printk("DATA0-->GPIO_DIR:%X,GPIO_DATA:%X\n",*GPIO23_00_DIR,*GPIO23_00_DATA);

    //如果需要做错误处理，请：goto mydrv_error;

    add_code_flag = 1;
    //----------------------  END  ---------------------------//

    goto init_success;

dev_reg_error:
    printk("alloc_chrdev_region failed\n");
    return ret_v;

cdev_add_error:
    printk("cdev_add failed\n");
    unregister_chrdev_region(dev_num, 1);
    init_flag = 0;
    return ret_v;

class_c_error:
    printk("class_create failed\n");
    cdev_del(&mydrv_cdev);
    unregister_chrdev_region(dev_num, 1);
    init_flag = 0;
    return PTR_ERR(mydrv_class);

device_c_error:
    printk("device_create failed\n");
    cdev_del(&mydrv_cdev);
    unregister_chrdev_region(dev_num, 1);
    class_destroy(mydrv_class);
    init_flag = 0;
    return PTR_ERR(mydrv_device);

//------------------ 请在此添加您的错误处理内容 ----------------//
mydrv_error:

    add_code_flag = 0;
    return -1;
//--------------------          END         -------------------//

init_success:
    printk("mydrv init success!\n");
    return 0;
}

//exit
static __exit void mydrv_exit(void)
{
    printk("mydrv drive exit...\n");

    if (add_code_flag == 1) {
        //----------   请在这里释放您的程序占有的资源   ---------//
        printk("free your resources...\n");

        iounmap(GPIOMODE);
        iounmap(GPIO39_24_DIR);
        iounmap(GPIO39_24_DATA);

        iounmap(GPIO71_40_DIR);
        iounmap(GPIO71_40_DATA);
        //		 iounmap(GPIO23_00_DIR);
        //		 iounmap(GPIO23_00_DATA);
        printk("free finish\n");
        //----------------------     END      -------------------//
    }

    if (init_flag == 1) {
        //释放初始化使用到的资源;
        cdev_del(&mydrv_cdev);
        unregister_chrdev_region(dev_num, 1);
        device_unregister(mydrv_device);
        class_destroy(mydrv_class);
    }
}

/**************** module operations**********************/
//module loading
module_init(mydrv_init);
module_exit(mydrv_exit);

//some infomation
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("from zxs");
MODULE_DESCRIPTION("ser485 drive");

/*********************  The End ***************************/
